package com.tools.middle.redis.client;

import com.fasterxml.jackson.core.type.TypeReference;
import com.tools.common.container.AbsOwnList;
import com.tools.common.container.OwnPairs;
import com.tools.common.object.C;
import com.tools.common.object.json.JsonKit;
import com.tools.common.object.Note;
import com.tools.common.object.ObjKit;
import com.tools.common.object.string.VerifyKit;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Redis 哈希类型的一些
 * 自定义封装的操作方法
 * */
@Note("Redis 哈希类型的一些自定义封装的操作方法")
abstract class TypeHash extends AbsJedis {

    TypeHash() {}


    /* **********************************************************************************
     *
     *          同名 - 需要手动 close 的操作方法
     *
     * *********************************************************************************
     * */

    public long hset(String key, String field, Object value) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        Long result = this.jedis().hset(key, field, ObjKit.toStringEmptyString(value));
        return DataTypeKit.default_returnResultLong(result);
    }

    public long hsetnx(String key, String field, Object value) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        Long result = this.jedis().hsetnx(key, field, ObjKit.toStringEmptyString(value));
        return DataTypeKit.default_returnResultLong(result);
    }


    public long hset(String key, Map fieldAndValues) {
        DataTypeKit.default_checkKey(key);
        Map<String, String> map = DataTypeKit.default_asStringValueMap(fieldAndValues);
        Long result = this.jedis().hset(key, map);
        return DataTypeKit.default_returnResultLong(result);
    }

    public long hset(String key, OwnPairs fieldAndValues) {
        DataTypeKit.default_checkKey(key);
        Map<String, String> map = DataTypeKit.default_asStringValueMap(fieldAndValues);
        Long result = this.jedis().hset(key, map);
        return DataTypeKit.default_returnResultLong(result);
    }

    public long hset(String key, String jsonObject) {
        if(!VerifyKit.isJsonObject(jsonObject)) {
            throw new IllegalArgumentException("【"+ jsonObject +"】 不是 JSON 对象类型");
        }
        DataTypeKit.default_checkKey(key);
        HashMap<String, String> map = JsonKit.toEntity(jsonObject, new C<HashMap<String, String>>() {});
        Long result = this.jedis().hset(key, map);
        return DataTypeKit.default_returnResultLong(result);
    }


    public long hset(String key, Object javaBean) {
        if(javaBean == null) throw new NullPointerException("要存储的 Java 对象为 null，无法获取其 [属性名称-属性值]");
        DataTypeKit.default_checkKey(key);
        HashMap<String, String> map = (HashMap<String, String>) ObjKit.toRealValue(javaBean, new C<HashMap<String, String>>() {});
        Long result = this.jedis().hset(key, map);
        return DataTypeKit.default_returnResultLong(result);
    }


    public Object hget(String key, String field) {
        DataTypeKit.default_checkKey(key);
        String result = this.jedis().hget(key, field);
        return ObjKit.stringToRealValue(result, true);
    }

    public <T>T hget(String key, String field, Class<T> valueType) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        String result = this.jedis().hget(key, field);
        return JsonKit.toEntity(result, valueType);
    }

    public <T>T hget(String key, String field, TypeReference<T> typeReference) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        String result = this.jedis().hget(key, field);
        return JsonKit.toEntity(result, typeReference);
    }

    public <T>T hget(String key, String field, C<T> valueType) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        String result = this.jedis().hget(key, field);
        return (T) ObjKit.toRealValue(result, valueType);
    }

    public Map<String, Object> hgetAll(String key) {
        DataTypeKit.default_checkKey(key);
        Map<String, String> res = this.jedis().hgetAll(key);
        return DataTypeKit.default_asStandardMap(res);
    }

    public <T>T hgetAll(String key, C<T> javaBeanType) {
        DataTypeKit.default_checkKey(key);
        Map<String, String> map = this.jedis().hgetAll(key);
        return (T) ObjKit.toRealValue(map, javaBeanType);
    }


    public String hmset(String key, Map fieldAndValues) {
        DataTypeKit.default_checkKey(key);
        Map<String, String> map = DataTypeKit.default_asStringValueMap(fieldAndValues);
        return this.jedis().hmset(key, map);
    }

    public String hmset(String key, OwnPairs fieldAndValues) {
        DataTypeKit.default_checkKey(key);
        Map<String, String> map = DataTypeKit.default_asStringValueMap(fieldAndValues);
        return this.jedis().hmset(key, map);
    }

    public String hmset(String key, String jsonObject) {
        if(!VerifyKit.isJsonObject(jsonObject)) {
            throw new IllegalArgumentException("【"+ jsonObject +"】 不是 JSON 对象类型");
        }
        DataTypeKit.default_checkKey(key);
        HashMap<String, String> map = JsonKit.toEntity(jsonObject, new C<HashMap<String, String>>() {});
        return this.jedis().hmset(key, map);
    }

    public String hmset(String key, Object javaBean) {
        if(javaBean == null) throw new NullPointerException("要存储的 Java 对象为 null，无法获取其 [属性名称-属性值]");
        DataTypeKit.default_checkKey(key);
        Map<String, String> map = (HashMap<String, String>) ObjKit.toRealValue(javaBean, new C<HashMap<String, String>>() {});
        return this.jedis().hmset(key, map);
    }

    public List<Object> hmget(String key, String... fields) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKeys(fields);
        List<String> result = this.jedis().hmget(key, fields);
        return DataTypeKit.default_asObjectList(result);
    }

    public List<Object> hmget(String key, Iterable<String> fields) {
        DataTypeKit.default_checkKey(key);
        List<String> result = this.jedis().hmget(key, DataTypeKit.default_asStringKeyArray(fields));
        return DataTypeKit.default_asObjectList(result);
    }

    public <T> List<T> hmget(String key, Class<T> valueType, String... fields) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKeys(fields);
        List<String> result = this.jedis().hmget(key, fields);
        return DataTypeKit.default_asSpecifyTypeList(result, valueType);
    }

    public <T> List<T> hmget(String key, Class<T> valueType, Iterable<String> fields) {
        DataTypeKit.default_checkKey(key);
        List<String> result = this.jedis().hmget(key, DataTypeKit.default_asStringKeyArray(fields));
        return DataTypeKit.default_asSpecifyTypeList(result, valueType);
    }


    public <T> List<T> hmget(String key, TypeReference<T> valueType, String... fields) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKeys(fields);
        List<String> result = this.jedis().hmget(key, fields);
        return DataTypeKit.default_asSpecifyTypeList(result, valueType);
    }

    public <T> List<T> hmget(String key, TypeReference<T> valueType, Iterable<String> fields) {
        DataTypeKit.default_checkKey(key);
        List<String> result = this.jedis().hmget(key, DataTypeKit.default_asStringKeyArray(fields));
        return DataTypeKit.default_asSpecifyTypeList(result, valueType);
    }


    public <T> List<T> hmget(String key, C<T> valueType, String... fields) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKeys(fields);
        List<String> result = this.jedis().hmget(key, fields);
        return DataTypeKit.default_asSpecifyTypeList(result, valueType);
    }

    public <T> List<T> hmget(String key, C<T> valueType, Iterable<String> fields) {
        DataTypeKit.default_checkKey(key);
        List<String> result = this.jedis().hmget(key, DataTypeKit.default_asStringKeyArray(fields));
        return DataTypeKit.default_asSpecifyTypeList(result, valueType);
    }


    public Map<String, Object> hmgetAsMap(String key, String... fields) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKeys(fields);
        List<String> res = this.jedis().hmget(key, fields);
        return DataTypeKit.default_hashResultAsMap(fields, res);
    }

    public Map<String, Object> hmgetAsMap(String key, List<String> fields) {
        DataTypeKit.default_checkKey(key);
        String[] fieldArray = DataTypeKit.default_asStringKeyArray(fields);
        List<String> res = this.jedis().hmget(key, fieldArray);
        return DataTypeKit.default_hashResultAsMap(fieldArray, res);
    }

    public Map<String, Object> hmgetAsMap(String key, AbsOwnList<String> fields) {
        DataTypeKit.default_checkKey(key);
        String[] fieldArray = DataTypeKit.default_asStringKeyArray(fields);
        List<String> res = this.jedis().hmget(key, fieldArray);
        return DataTypeKit.default_hashResultAsMap(fieldArray, res);
    }


    public <T>T hmgetAsBean(String key, C<T> beanType, String... fields) {
        Map<String, Object> map = this.hmgetAsMap(key, fields);
        return ObjKit.buildObject(beanType, map);
    }

    public <T>T hmgetAsBean(String key, C<T> beanType, List<String> fields) {
        Map<String, Object> map = this.hmgetAsMap(key, fields);
        return ObjKit.buildObject(beanType, map);
    }

    public <T>T hmgetAsBean(String key, C<T> beanType, AbsOwnList<String> fields) {
        Map<String, Object> map = this.hmgetAsMap(key, fields);
        return ObjKit.buildObject(beanType, map);
    }


    public long hdel(String key, String field) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        Long result = this.jedis().hdel(key, field);
        return DataTypeKit.default_returnResultLong(result);
    }

    public long hdel(String key, String... fields) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKeys(fields);
        Long result = this.jedis().hdel(key, fields);
        return DataTypeKit.default_returnResultLong(result);
    }

    public long hdel(String key, Iterable<String> fields) {
        DataTypeKit.default_checkKey(key);
        Long result = this.jedis().hdel(key, DataTypeKit.default_asStringKeyArray(fields));
        return DataTypeKit.default_returnResultLong(result);
    }


    public Set<String> hkeys(String key) {
        DataTypeKit.default_checkKey(key);
        return this.jedis().hkeys(key);
    }

    public boolean hexists(String key, String field) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        Boolean result = this.jedis().hexists(key, field);
        return result != null && result;
    }

    public long hlen(String key) {
        DataTypeKit.default_checkKey(key);
        Long result = this.jedis().hlen(key);
        return DataTypeKit.default_returnResultLong(result);
    }

    public List<Object> hvals(String key) {
        DataTypeKit.default_checkKey(key);
        List<String> values = this.jedis().hvals(key);
        return DataTypeKit.default_asObjectList(values);
    }

    public <T> List<T> hvals(String key, Class<T> valueType) {
        DataTypeKit.default_checkKey(key);
        List<String> values = this.jedis().hvals(key);
        return DataTypeKit.default_asSpecifyTypeList(values, valueType);
    }

    public <T> List<T> hvals(String key, TypeReference<T> valueType) {
        DataTypeKit.default_checkKey(key);
        List<String> values = this.jedis().hvals(key);
        return DataTypeKit.default_asSpecifyTypeList(values, valueType);
    }

    public <T> List<T> hvals(String key, C<T> valueType) {
        DataTypeKit.default_checkKey(key);
        List<String> values = this.jedis().hvals(key);
        return DataTypeKit.default_asSpecifyTypeList(values, valueType);
    }

    @Note("随机返回一个 field")
    public String hrandfield(String key) {
        DataTypeKit.default_checkKey(key);
        return this.jedis().hrandfield(key);
    }

    @Note("随机返回 count 个 field，若 count 为正结果集不重复，为负则可重复")
    public List<String> hrandfield(String key, long count) {
        DataTypeKit.default_checkKey(key);
        return this.jedis().hrandfield(key, count);
    }

    @Note("随机获取 count 个 hashKey-value")
    public Map<String, Object> hrandfieldWithValue(String key, long count) {
        DataTypeKit.default_checkKey(key);
        Map<String, String> res = this.jedis().hrandfieldWithValues(key, count);
        return DataTypeKit.default_asStandardMap(res);
    }

    public <T> T hrandfieldWithValue(String key, long count, C<T> javaBeanType) {
        Map<String, Object> map = this.hrandfieldWithValue(key, count);
        return (T) ObjKit.toRealValue(map, javaBeanType);
    }


    public long hstrlen(String key, String field) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        Long result = this.jedis().hstrlen(key, field);
        return DataTypeKit.default_returnResultLong(result);
    }

    @Note("为 field 的值自增 value 个增量，如果 field 的值不存在则设置为 0，若不为字符串则抛异常")
    public long hincrBy(String key, String field, long value) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        Long result = this.jedis().hincrBy(key, field, value);
        return DataTypeKit.default_returnResultLong(result);
    }

    public double hincrByFloat(String key, String field, double value) {
        DataTypeKit.default_checkKey(key);
        DataTypeKit.default_checkKey(field);
        Double result = this.jedis().hincrByFloat(key, field, value);
        return result == null ? 0.0 : result;
    }

    @Note("游标 cursor 的初始值为【0】，当返回的游标值再度为【0】 时才代表整体遍历结束" +
            "返回值是批量的 key 和下一次开始游标。" +
            "返回的结果可能会有重复，需要自己去重")
    public ScanResult<Map.Entry<String, String>> hscan(String key, String cursor) {
        DataTypeKit.default_checkKey(key);
        if(cursor == null || cursor.isEmpty()) cursor = "0";
        return this.jedis().hscan(key, cursor);
    }

    @Note("ScanParams 是可以设置 count（返回 key 的个数）和 match（正则匹配）的")
    public ScanResult<Map.Entry<String, String>> hscan(String key, String cursor, ScanParams params) {
        DataTypeKit.default_checkKey(key);
        if(cursor == null || cursor.isEmpty()) cursor = "0";
        if(params == null) return hscan(key, cursor);
        return this.jedis().hscan(key, cursor, params);
    }

    /* **********************************************************************************
     *
     *          带【$】 - 自动 close 的操作方法
     *
     * *********************************************************************************
     * */

    public long hset$(String key, String field, Object value) {
        try {
            return this.hset(key, field, value);
        } finally {
            this.close();
        }
    }


    public long hsetnx$(String key, String field, Object value) {
        try {
            return this.hsetnx(key, field, value);
        } finally {
            this.close();
        }
    }


    public long hset$(String key, Map fieldAndValues) {
        try {
            return this.hset(key, fieldAndValues);
        } finally {
            this.close();
        }
    }

    public long hset$(String key, OwnPairs fieldAndValues) {
        try {
            return this.hset(key, fieldAndValues);
        } finally {
            this.close();
        }
    }

    public long hset$(String key, String jsonObject) {
        try {
            return this.hset(key, jsonObject);
        } finally {
            this.close();
        }
    }


    public long hset$(String key, Object javaBean) {
        try {
            return this.hset(key, javaBean);
        } finally {
            this.close();
        }
    }

    public Object hget$(String key, String field) {
        try {
            return this.hget(key, field);
        } finally {
            this.close();
        }
    }

    public <T> T hget$(String key, String field, Class<T> valueType) {
        try {
            return this.hget(key, field, valueType);
        } finally {
            this.close();
        }
    }

    public <T> T hget$(String key, String field, TypeReference<T> typeReference) {
        try {
            return this.hget(key, field, typeReference);
        } finally {
            this.close();
        }
    }

    public <T> T hget$(String key, String field, C<T> valueType) {
        try {
            return this.hget(key, field, valueType);
        } finally {
            this.close();
        }
    }

    public Map<String, Object> hgetAll$(String key) {
        try {
            return this.hgetAll(key);
        } finally {
            this.close();
        }
    }

    public <T> T hgetAll$(String key, C<T> javaBeanType) {
        try {
            return this.hgetAll(key, javaBeanType);
        } finally {
            this.close();
        }
    }

    public String hmset$(String key, Map fieldAndValues) {
        try {
            return this.hmset(key, fieldAndValues);
        } finally {
            this.close();
        }
    }

    public String hmset$(String key, OwnPairs fieldAndValues) {
        try {
            return this.hmset(key, fieldAndValues);
        } finally {
            this.close();
        }
    }

    public String hmset$(String key, String jsonObject) {
        try {
            return this.hmset(key, jsonObject);
        } finally {
            this.close();
        }
    }

    public String hmset$(String key, Object javaBean) {
        try {
            return this.hmset(key, javaBean);
        } finally {
            this.close();
        }
    }


    public List<Object> hmget$(String key, String... fields) {
        try {
            return this.hmget(key, fields);
        } finally {
            this.close();
        }
    }

    public List<Object> hmget$(String key, Iterable<String> fields) {
        try {
            return this.hmget(key, fields);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hmget$(String key, Class<T> valueType, String... fields) {
        try {
            return this.hmget(key, valueType, fields);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hmget$(String key, Class<T> valueType, Iterable<String> fields) {
        try {
            return this.hmget(key, valueType, fields);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hmget$(String key, TypeReference<T> valueType, String... fields) {
        try {
            return this.hmget(key, valueType, fields);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hmget$(String key, TypeReference<T> valueType, Iterable<String> fields) {
        try {
            return this.hmget(key, valueType, fields);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hmget$(String key, C<T> valueType, String... fields) {
        try {
            return this.hmget(key, valueType, fields);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hmget$(String key, C<T> valueType, Iterable<String> fields) {
        try {
            return this.hmget(key, valueType, fields);
        } finally {
            this.close();
        }
    }

    public Map<String, Object> hmgetAsMap$(String key, String... fields) {
        try {
            return this.hmgetAsMap(key, fields);
        } finally {
            this.close();
        }
    }

    public Map<String, Object> hmgetAsMap$(String key, List<String> fields) {
        try {
            return this.hmgetAsMap(key, fields);
        } finally {
            this.close();
        }
    }

    public Map<String, Object> hmgetAsMap$(String key, AbsOwnList<String> fields) {
        try {
            return this.hmgetAsMap(key, fields);
        } finally {
            this.close();
        }
    }

    public <T> T hmgetAsBean$(String key, C<T> beanType, String... fields) {
        try {
            return this.hmgetAsBean(key, beanType, fields);
        } finally {
            this.close();
        }
    }

    public <T> T hmgetAsBean$(String key, C<T> beanType, List<String> fields) {
        try {
            return this.hmgetAsBean(key, beanType, fields);
        } finally {
            this.close();
        }
    }

    public <T> T hmgetAsBean$(String key, C<T> beanType, AbsOwnList<String> fields) {
        try {
            return this.hmgetAsBean(key, beanType, fields);
        } finally {
            this.close();
        }
    }

    public long hdel$(String key, String field) {
        try {
            return this.hdel(key, field);
        } finally {
            this.close();
        }
    }

    public long hdel$(String key, String... field) {
        try {
            return this.hdel(key, field);
        } finally {
            this.close();
        }
    }

    public long hdel$(String key, Iterable<String> field) {
        try {
            return this.hdel(key, field);
        } finally {
            this.close();
        }
    }

    public Set<String> hkeys$(String key) {
        try {
            return this.hkeys(key);
        } finally {
            this.close();
        }
    }

    public boolean hexists$(String key, String field) {
        try {
            return this.hexists(key, field);
        } finally {
            this.close();
        }
    }

    public long hlen$(String key) {
        try {
            return this.hlen(key);
        } finally {
            this.close();
        }
    }

    public List<Object> hvals$(String key) {
        try {
            return this.hvals(key);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hvals$(String key, Class<T> valueType) {
        try {
            return this.hvals(key, valueType);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hvals$(String key, TypeReference<T> valueType) {
        try {
            return this.hvals(key, valueType);
        } finally {
            this.close();
        }
    }

    public <T> List<T> hvals$(String key, C<T> valueType) {
        try {
            return this.hvals(key, valueType);
        } finally {
            this.close();
        }
    }

    public String hrandfield$(String key) {
        try {
            return this.hrandfield(key);
        } finally {
            this.close();
        }
    }

    public List<String> hrandfield$(String key, long count) {
        try {
            return this.hrandfield(key, count);
        } finally {
            this.close();
        }
    }

    public Map<String, Object> hrandfieldWithValue$(String key, long count) {
        try {
            return this.hrandfieldWithValue(key, count);
        } finally {
            this.close();
        }
    }

    public <T> T hrandfieldWithValue$(String key, long count, C<T> javaBeanType) {
        try {
            return this.hrandfieldWithValue(key, count, javaBeanType);
        } finally {
            this.close();
        }
    }

    public long hstrlen$(String key, String field) {
        try {
            return this.hstrlen(key, field);
        } finally {
            this.close();
        }
    }

    public long hincrBy$(String key, String field, long value) {
        try {
            return this.hincrBy(key, field, value);
        } finally {
            this.close();
        }
    }

    public double hincrByFloat$(String key, String field, double value) {
        try {
            return this.hincrByFloat(key, field, value);
        } finally {
            this.close();
        }
    }

    public ScanResult<Map.Entry<String, String>> hscan$(String key, String cursor) {
        try {
            return this.hscan(key, cursor);
        } finally {
            this.close();
        }
    }

    public ScanResult<Map.Entry<String, String>> hscan$(String key, String cursor, ScanParams params) {
        try {
            return this.hscan(key, cursor, params);
        } finally {
            this.close();
        }
    }

}
